package ia;

	import java.util.ArrayList;
import java.util.Collections;


   public class Nodo45 extends Nodo implements Comparable<Nodo45> {
	    
	    private Nodo45            _padre;
	    private ArrayList<Nodo45> _hijos = new ArrayList<Nodo45>();

	    public Nodo45(int x, int y, Orientacion o){
	        super(x,y,o);        
	    	_padre = null;
	    }

	    public void setPadre(Nodo nPadre){
	        _padre = (Nodo45) nPadre;
	    }
	    
	    public Nodo45 getPadre(){
	        return _padre;
	    }

	    public ArrayList<Nodo45> getHijos(){
	        return _hijos;
	    }
	    
	    // obtiene los antecesores de un nodo
	    public ArrayList<Nodo45> getAntecesores(){
	        ArrayList<Nodo45> antecesores = new ArrayList<Nodo45>();
	        Nodo45 padre = this.getPadre();
	        while(padre!=null){
	            antecesores.add(padre);
	            padre = padre.getPadre();
	        }
	        return antecesores;
	    }
	    
	    // obtiene la profundidad de un nodo
	    public int getProfundidad(){
	        int profundidad = 0;
	        Nodo45 padre = this.getPadre();
	        while(padre!=null){
	            profundidad++;
	            padre = padre.getPadre();
	        }
	        return profundidad;
	    }

	    // indica si es posible moverse en la direccion
	    public boolean puedeMover(int[][] matriz){
	    	
	        int ultimaFila    = matriz.length-1;
	        int ultimaColumna = matriz[0].length-1;

	        if ((_or == Orientacion.N)  & (_x == 0))                                    return false;
	        if ((_or == Orientacion.E)  & (_y == ultimaColumna))                        return false;
	        if ((_or == Orientacion.S)  & (_x == ultimaFila))                           return false;
	        if ((_or == Orientacion.O)  & (_y == 0))                                    return false;
	        if ((_or == Orientacion.SO) & ((_x == ultimaFila) | (_y == 0)))             return false;
	        if ((_or == Orientacion.SE) & ((_x == ultimaFila) | (_y == ultimaColumna))) return false;
	        if ((_or == Orientacion.NE) & ((_x == 0) | (_y == ultimaColumna)))          return false;
	        if ((_or == Orientacion.NO) & ((_x == 0) | (_y == 0)))                      return false;
	        
	        return true;
	    }

	    public Nodo45 mover(int[][]matriz, Orientacion orientacion){
	        Nodo45 nodo = null;        
	        int fila    = 0;
	        int columna = 0;
	        
	        if (orientacion == Orientacion.N)  nodo = new Nodo45(fila=_x-1,columna=_y,Orientacion.N);
	        if (orientacion == Orientacion.O)  nodo = new Nodo45(fila=_x,columna=_y-1,Orientacion.O);
	        if (orientacion == Orientacion.S)  nodo = new Nodo45(fila=_x+1,columna=_y,Orientacion.S);
	        if (orientacion == Orientacion.E)  nodo = new Nodo45(fila=_x,columna=_y+1,Orientacion.E);
	        if (orientacion == Orientacion.NE) nodo = new Nodo45(fila=_x-1,columna=_y+1,Orientacion.NE);
	        if (orientacion == Orientacion.NO) nodo = new Nodo45(fila=_x-1,columna=_y-1,Orientacion.NO);
	        if (orientacion == Orientacion.SE) nodo = new Nodo45(fila=_x+1,columna=_y+1,Orientacion.SE);
	        if (orientacion == Orientacion.SO) nodo = new Nodo45(fila=_x+1,columna=_y-1,Orientacion.SO);
	        
	        nodo.setTransicionCoste(matriz[fila][columna]);
	        nodo.setCoste(_coste+nodo.getTransicionCoste());
	        
	        _hijos.add(nodo);
	        return nodo;
	    }
	    
	    public ArrayList<Nodo45> aplicaOperadores(int[][] matrizPeligrosidades){
	       ArrayList<Nodo45> sucesores = new ArrayList<Nodo45>();
	       Nodo45 giroIzq;  // Nodo girando en sentido antihorario
	       Nodo45 giroDer;  // Nodo girando en sentido horario
	       boolean posibleMover = this.puedeMover(matrizPeligrosidades);
	       float costePadre = _coste;
	       if (_or == Orientacion.N) {
	   				giroIzq = iniciarNodo(Orientacion.NO,costePadre);
	   				giroDer = iniciarNodo(Orientacion.NE,costePadre);
	   				sucesores.add(giroIzq);
	   				sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.N));
	       }
	       if (_or == Orientacion.S) {
	   				giroIzq = iniciarNodo(Orientacion.SE,costePadre);
	   				giroDer = iniciarNodo(Orientacion.SO,costePadre);
	   				sucesores.add(giroIzq);
	   				sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.S));
	       } 
	       if (_or == Orientacion.E) {
	   				giroIzq = iniciarNodo(Orientacion.NE,costePadre);
	   				giroDer = iniciarNodo(Orientacion.SE,costePadre);
	   				sucesores.add(giroIzq);
	   				sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.E));
	       }
	       if (_or == Orientacion.O) {
	   				giroIzq = iniciarNodo(Orientacion.SO,costePadre);
	   				giroDer = iniciarNodo(Orientacion.NO,costePadre);
	   				sucesores.add(giroIzq);
	   				sucesores.add(giroDer);	   
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.O));
	       }
	       if (_or == Orientacion.NO) {
	   				giroIzq = iniciarNodo(Orientacion.O,costePadre);
	   				giroDer = iniciarNodo(Orientacion.N,costePadre);
	   				sucesores.add(giroIzq);
	   				sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.NO));
	       }
	       if (_or == Orientacion.NE) {
   	   				giroIzq = iniciarNodo(Orientacion.E,costePadre);
   	   				giroDer = iniciarNodo(Orientacion.N,costePadre);
   	   				sucesores.add(giroIzq);
   	   				sucesores.add(giroDer);               
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.NE));
	       }
	       if (_or == Orientacion.SE) {
	    	   		giroIzq = iniciarNodo(Orientacion.S,costePadre);
	    	   		giroDer = iniciarNodo(Orientacion.E,costePadre);
	                sucesores.add(giroIzq);
	                sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.SE));
	       }
	       if (_or == Orientacion.SO) {
	    	   		giroIzq = iniciarNodo(Orientacion.S,costePadre);
	    	   		giroDer = iniciarNodo(Orientacion.O,costePadre);
	                sucesores.add(giroIzq);
	                sucesores.add(giroDer);
	                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.SO));
	       }
	      
	      _hijos.addAll(sucesores);
	      return sucesores;
	    }
	    
	    // crea un nuevo nodo
	    private Nodo45 iniciarNodo(Orientacion or,float costePadre) {
	    	
	    	Nodo45 aux = new Nodo45(_x,_y,or);
	    	aux.setCoste(costePadre+1);
	    	aux.setTransicionCoste(1);
	    	
	    	return aux;
	    }


	    public void mostrarResultado(ArrayList<Nodo45> cerrados){
	    	
	        if (cerrados!=null) {
	            ArrayList<Nodo45> recorrido = new ArrayList<Nodo45>();
	            recorrido.addAll(this.getAntecesores());
	            Collections.reverse(recorrido); // el recorrido se encuentra al revés
	            recorrido.add(this);            // añadimos el nodo actual
	            float mejorCoste = this.getCoste();
	            System.out.println("Nodo final:                   "+this);
	            System.out.println("Recorrido:                    "+recorrido);
	            recorrido.clear();
	            int profundidad = this.getProfundidad();
	            System.out.println("Profundidad:                  "+profundidad);
	            System.out.println("Coste:                        "+mejorCoste);
	            System.out.println("Factor de ramificación:       "+BranchingFactor.compute(cerrados.size(), profundidad));	
	            System.out.println("Nodos cerrados seleccionados: "+cerrados.size());
        	
	        }
	        else
	        	System.out.println("La solucion del problema es el nodo principal");
	    }
	    
	    @Override
	    public boolean equals(Object nodo){
	        if(nodo instanceof Nodo45){
	            Nodo45 n = (Nodo45)nodo;
	            if((n.getX() == _x) & (n.getY() == _y) & (n.getOrientacion() == _or)) return true;
	            else 
	            	 return false;
	        }else 
	        	 return false;
	    }
	    
	    @Override
	    public int compareTo(Nodo45  nodo) {
	        if(this.getFinal() < nodo.getFinal())       return -1;
	        else if(this.getFinal() > nodo.getFinal())  return  1;
	        else return 0;
	    }
	    

	    @Override
	    public String toString(){
	        String anchura   = "("+_x+","+_y+","+_or+")";
	        String Aestrella = "ID:"+_id+"("+_x+","+_y+","+_or+")"
	                +" g="+_coste
	                +" h="+_valorH;
	        
	        if (_valorH < 0) 
	        	return anchura;
	        else 
	        	return Aestrella;
	    }

}
